package com.github.ghsea.sso.client.config;

import com.github.ghsea.sso.client.common.SsoJedisPool;
import com.github.ghsea.sso.client.security.cas.CasFilterExt;
import com.github.ghsea.sso.client.security.cas.ExtCasRealm;
import com.github.ghsea.sso.client.security.shiro.FixedPathSimpleCookie;
import com.github.ghsea.sso.client.security.shiro.JedisCacheManager;
import com.github.ghsea.sso.client.security.shiro.RedisSessionDAO;
import com.github.ghsea.sso.client.security.shiro.SessionManager;
import com.github.ghsea.sso.client.security.shiro.UUIDSessionIdGenerator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import javax.servlet.Filter;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import redis.clients.jedis.JedisPoolConfig;

/**
 * @author guhai
 * @since 2021/6/12 16:26
 */
@Configuration
@ComponentScan(value = "com.github.ghsea.sso.client")
@DependsOn(value = {"shiroConfig"})
public class ShrioBeansConfiguration implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager, ShiroConfig shiroConfig) {
        ShiroFilterFactoryBean shiroFilterBean = new ShiroFilterFactoryBean();
        shiroFilterBean.setSecurityManager(securityManager);

        /**
         * 配置拦截器
         */
        Map<String, Filter> filters = new HashMap<>();
        filters.put("cas", applicationContext.getBean("casFilter", Filter.class));
        filters.put("logout", applicationContext.getBean("logoutFilter", Filter.class));
        filters.put("default", applicationContext.getBean("defaultFilter", Filter.class));
        shiroFilterBean.setFilters(filters);

        /**
         * 配置拦截器和Path的映射关系
         */
        Set<String> anonymousPaths = shiroConfig.getPathsWithoutLogin();
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        if (anonymousPaths != null && anonymousPaths.size() > 0) {
            anonymousPaths.forEach(e -> filterChainDefinitionMap.put(e, "anon"));
        }

        filterChainDefinitionMap.put(shiroConfig.getApplicationPath() + "/cas", "cas");
        filterChainDefinitionMap.put(shiroConfig.getApplicationPath() + "/logout", "logout");
        filterChainDefinitionMap.put(shiroConfig.getApplicationPath() + "/**", "default");
        shiroFilterBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        return shiroFilterBean;
    }


    @Bean(name = "myCasRealm")
    public ExtCasRealm extCasRealm(JedisCacheManager cacheManager, ShiroConfig shiroConfig) {
        ExtCasRealm casRealm = new ExtCasRealm();
        casRealm.setCasServerUrlPrefix(shiroConfig.getCasServerUrl());
        casRealm.setCacheManager(cacheManager);
        casRealm.setEncoding("UTF-8");
        return casRealm;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor advisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

    @Bean
    public SecurityManager securityManager(ExtCasRealm realm, SessionManager sessionManager) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(realm);
        securityManager.setSessionManager(sessionManager);
        return securityManager;
    }

    @Bean
    public SessionManager sessionManager(RedisSessionDAO redisSessionDAO, FixedPathSimpleCookie sessionIdCookie, ShiroConfig shiroConfig) {
        SessionManager sessionManager = new SessionManager();
        sessionManager.setSessionDAO(redisSessionDAO);
        sessionManager.setGlobalSessionTimeout(shiroConfig.getSessionTimeoutMs());
        sessionManager.setSessionValidationInterval(shiroConfig.getSessionTimeoutMs());
        sessionManager.setSessionValidationSchedulerEnabled(true);
        sessionManager.setSessionIdCookie(sessionIdCookie);
        sessionManager.setSessionIdCookieEnabled(true);
        return sessionManager;
    }

    @Bean(name = "casFilter")
    public CasFilterExt casFilterExt(ShiroConfig shiroConfig) {
        CasFilterExt casFilterExt = new CasFilterExt();
        casFilterExt.setFailureUrl(shiroConfig.getCasServerUrl() + "/login");
        return casFilterExt;
    }

    @Bean(name = "sessionDAO")
    public RedisSessionDAO sessionDAO(UUIDSessionIdGenerator sessionIdGenerator, SsoJedisPool jedisPool) {
        RedisSessionDAO sessionDAO = new RedisSessionDAO();
        sessionDAO.setSessionIdGenerator(sessionIdGenerator);
        sessionDAO.setJedisPool(jedisPool);
        return sessionDAO;
    }

    @Bean(name = "sessionIdCookie")
    public FixedPathSimpleCookie cookieConfig(ShiroConfig shiroConfig) {
        FixedPathSimpleCookie simpleCookie = new FixedPathSimpleCookie();
        simpleCookie.setName(shiroConfig.getSessionKey());
        simpleCookie.setDomain(shiroConfig.getCookieDomain());
        simpleCookie.setHttpOnly(shiroConfig.isHttpOnly());
        simpleCookie.setSameSite("none");

        if (shiroConfig.isHttps()) {
            simpleCookie.setSecure(true);
        }
        return simpleCookie;
    }

    @Bean
    public SsoJedisPool SsoJedisPool(ShiroConfig shiroConfig) {
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMaxIdle(10);
        poolConfig.setMaxTotal(100);
        poolConfig.setTestOnBorrow(true);

        return new SsoJedisPool(poolConfig,
                shiroConfig.getRedisHost(),
                shiroConfig.getRedisPort(),
                5000, shiroConfig.getRedisPassword());
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    /**
     * 开启shiro的注解(如@RequiresRoles,@RequiresPermissions)
     *
     * @return
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    @Bean
    @DependsOn({"lifecycleBeanPostProcessor"})
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    /**
     * 开启aop注解支持
     *
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }


}
